class MIDITrack {
  ArrayList<MIDINote> pendingNotes;
  ArrayList<MIDINote> closedNotes;
  ArrayList<Integer> pendingRemoval;
  PVector timeSignature;
  String name;
  Instrument instrument;
  int instrumentNumber;
  String instrumentName;

  MIDITrack() {
    pendingNotes = new ArrayList<MIDINote>();
    closedNotes = new ArrayList<MIDINote>();
    pendingRemoval = new ArrayList<Integer>();
  }

  void addNote(String noteName, int key, int velocity, long tick) {
    MIDINote note = new MIDINote(noteName, key, velocity, tick);
    if (debug) println("# adding note: " + key + " at "+ tick);
    long lastTick = getLatestTick();
    if (lastTick < note.startTick) {
      fillRests(lastTick, note.startTick);
    }
    pendingNotes.add(note);
  }

  void addClosedNote(float duration, MIDINote note) {
    note.closed = true;
    note.duration = duration;
    note.endTick = note.startTick + round(duration*4*timeResolution);
    if (debug) println("# adding closed note: " + note.key + " at "+ note.startTick);
    closedNotes.add(note);
  }

  void addRest(float duration, long startTick) {
    if (debug) println("adding rest of dur: " + duration);
    MIDINote rest = new MIDINote("r", 128, 0, startTick);
    rest.closed = true;
    rest.duration = duration;
    rest.endTick = startTick + round(duration*4*timeResolution);
    closedNotes.add(rest);
  }

  void closeNote(String noteName, int key, int velocity, long tick) {
    for (int i = 0; i < pendingNotes.size(); i++) {
      if (pendingNotes.get(i).key == key && !pendingNotes.get(i).closed) {
        if (debug) println("# 1 closing note: " + key + " at "+ tick );
        MIDINote newNote = pendingNotes.get(i);
        pendingNotes.get(i).closed = true;
        long delta = tick - newNote.startTick;
        fillNotes(newNote, delta);
      }
    }
//    for (int n : pendingRemoval) {
//      pendingNotes.remove(n);
//    }
//    pendingRemoval.clear();
  }

  long getLatestTick() {
    long tick = 0;
    for (int i = 0; i < closedNotes.size(); i++) {
      if (closedNotes.get(i).endTick > tick) {
        tick = closedNotes.get(i).endTick;
      }
    }
    return tick;
  }

  void fillRests(long from, long to) {
    long count = to - from;
    long startTick = from;

    long fullNotes = count/(timeResolution*4);
    count = count%(timeResolution*4); 

    long halfNotes = count/(timeResolution*2);
    count = count%(timeResolution*2);       

    long forthNotes = count/timeResolution;
    count = count%timeResolution;

    long eithNotes = count/(timeResolution/2);
    count = count%(timeResolution/2);

    long sixteenthNotes = count/(timeResolution/4);
    count = count%(timeResolution/4);

    long thNotes = count/(timeResolution/8);
    count = count%(timeResolution/8);

    long sfNotes = count/(timeResolution/16);

    for (long a = 0; a < fullNotes; a++) {
      addRest(1, startTick);
      startTick += timeResolution*4;
    }
    for (long b = 0; b < halfNotes; b++) {
      addRest(0.5, startTick);
      startTick += timeResolution*2;
    }
    for (long c = 0; c < forthNotes; c++) {
      addRest(0.25, startTick);
      startTick += timeResolution;
    }
    for (long d = 0; d < eithNotes; d++) {
      addRest(0.125, startTick);
      startTick += timeResolution/2;
    }
    for (long e = 0; e < sixteenthNotes; e++) {
      addRest(0.0625, startTick);
      startTick += timeResolution/4;
    }
    for (long f = 0; f < thNotes; f++) {
      addRest(0.03125, startTick);
      startTick += timeResolution/8;
    }
    for (long g = 0; g < sfNotes; g++) {
      addRest(0.015625, startTick);
      startTick += timeResolution/16;
    }
  }

  void fillNotes(MIDINote note, long ticks) {
    //println(" # fill Notes " + note.key+ " " + ticks);
    long count = ticks;
    long startTick = note.startTick;
    
    long fullNotes = count/(timeResolution*4);
    count = count%(timeResolution*4);
    
    long halfNotes  = count/(timeResolution*2);
    count = count%(timeResolution*2);   
    
    long forthNotes = count/timeResolution;
    count = count%timeResolution;
    
    long eithNotes = count/(timeResolution/2);
    count = count%(timeResolution/2);
    
    long sixteenthNotes = count/(timeResolution/4);
    count = count%(timeResolution/4);
    
    long thNotes = count/(timeResolution/8);
    count = count%(timeResolution/8);
    
    long sfNotes = count/(timeResolution/16);

    for (long a = 0; a < fullNotes; a++) {
      addClosedNote(1.0, note);
      startTick += timeResolution*4;
    }
    for (long b = 0; b < halfNotes; b++) {
      addClosedNote(0.5, note);
      startTick += timeResolution*2;
    }
    for (long c = 0; c < forthNotes; c++) {
      addClosedNote(0.25, note);
      startTick += timeResolution;
    }
    for (long d = 0; d < eithNotes; d++) {
      addClosedNote(0.125, note);
      startTick += timeResolution/2;
    }
    for (long e = 0; e < sixteenthNotes; e++) {
      addClosedNote(0.0625, note);
      startTick += timeResolution/4;
    }
    for (long f = 0; f < thNotes; f++) {
      addClosedNote(0.03125, note);
      startTick += timeResolution/8;
    }
    for (long g = 0; g < sfNotes; g++) {
      addClosedNote(0.015625, note);
      startTick += timeResolution/16;
    }
  }
}

